<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.2">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha256-HtsXJanqjKTc8vVQjO4YMhiqFoXkfBsjBWcX91T1jr8=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"xiaoqingming18.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.15.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="事件循环浏览器的进程模型何为进程？程序运行需要有它自己专属的内存空间，可以把这块内存空间理解为进程。 每个应用至少有一个进程，进程之间相互独立，即使要通信，也需要双方同意。 何为线程？有了进程后，就可以运行程序的代码了。 运行代码的“人”称之为“线程”。 一个进程至少有一个线程，所以在进程开启后会自动创建一个线程来运行代码，该线程称之为主线程。 如果程序需要同时执行多块代码，主线程就会启动更多的线">
<meta property="og:type" content="article">
<meta property="og:title" content="WEB前端大师课笔记">
<meta property="og:url" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/index.html">
<meta property="og:site_name" content="鸣蜩十七">
<meta property="og:description" content="事件循环浏览器的进程模型何为进程？程序运行需要有它自己专属的内存空间，可以把这块内存空间理解为进程。 每个应用至少有一个进程，进程之间相互独立，即使要通信，也需要双方同意。 何为线程？有了进程后，就可以运行程序的代码了。 运行代码的“人”称之为“线程”。 一个进程至少有一个线程，所以在进程开启后会自动创建一个线程来运行代码，该线程称之为主线程。 如果程序需要同时执行多块代码，主线程就会启动更多的线">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/浏览器多进程多线程.png">
<meta property="og:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/排队.png">
<meta property="og:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/异步.png">
<meta property="og:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染流水线.png">
<meta property="og:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染完整过程.png">
<meta property="article:published_time" content="2023-06-10T13:02:29.000Z">
<meta property="article:modified_time" content="2023-06-11T09:34:51.115Z">
<meta property="article:author" content="鸣蜩十七">
<meta property="article:tag" content="-事件循环">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/浏览器多进程多线程.png">


<link rel="canonical" href="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/","path":"2023/06/10/WEB前端大师课笔记/","title":"WEB前端大师课笔记"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>WEB前端大师课笔记 | 鸣蜩十七</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>

<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">鸣蜩十七</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">鸣蜩十七是和女朋友谈恋爱的日子</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li><li class="menu-item menu-item-schedule"><a href="/schedule/" rel="section"><i class="fa fa-calendar fa-fw"></i>日程表</a></li><li class="menu-item menu-item-sitemap"><a href="/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-commonweal"><a href="/404/" rel="section"><i class="fa fa-heartbeat fa-fw"></i>公益 404</a></li>
  </ul>
</nav>




</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF"><span class="nav-text">事件循环</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%A8%A1%E5%9E%8B"><span class="nav-text">浏览器的进程模型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%95%E4%B8%BA%E8%BF%9B%E7%A8%8B%EF%BC%9F"><span class="nav-text">何为进程？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%95%E4%B8%BA%E7%BA%BF%E7%A8%8B%EF%BC%9F"><span class="nav-text">何为线程？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9C%89%E5%93%AA%E4%BA%9B%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%EF%BC%9F"><span class="nav-text">浏览器有哪些进程和线程？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E4%B8%BB%E7%BA%BF%E7%A8%8B%E6%98%AF%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%E7%9A%84"><span class="nav-text">渲染主线程是如何工作的</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%8B%A5%E5%B9%B2%E8%A7%A3%E9%87%8A"><span class="nav-text">若干解释</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%95%E4%B8%BA%E5%BC%82%E6%AD%A5"><span class="nav-text">何为异步</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#JS%E4%B8%BA%E4%BD%95%E4%BC%9A%E9%98%BB%E5%A1%9E%E6%B8%B2%E6%9F%93"><span class="nav-text">JS为何会阻塞渲染</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%BB%E5%8A%A1%E6%9C%89%E4%BC%98%E5%85%88%E7%BA%A7%E5%90%97"><span class="nav-text">任务有优先级吗</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-text">总结</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86"><span class="nav-text">浏览器渲染原理</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E6%98%AF%E5%A6%82%E4%BD%95%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E7%9A%84"><span class="nav-text">浏览器是如何渲染页面的</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E7%9A%84%E7%AC%AC%E4%B8%80%E6%AD%A5%EF%BC%9A%E8%A7%A3%E6%9E%90HTML%E2%80%94%E2%80%94Parse-HTML"><span class="nav-text">渲染的第一步：解析HTML——Parse HTML</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E7%9A%84%E7%AC%AC%E4%BA%8C%E6%AD%A5%EF%BC%9A%E6%A0%B7%E5%BC%8F%E8%AE%A1%E7%AE%97%E2%80%94%E2%80%94Recalculate-Style"><span class="nav-text">渲染的第二步：样式计算——Recalculate Style</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E7%9A%84%E7%AC%AC%E4%B8%89%E6%AD%A5%EF%BC%9A%E5%B8%83%E5%B1%80"><span class="nav-text">渲染的第三步：布局</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E7%9A%84%E7%AC%AC%E5%9B%9B%E6%AD%A5%EF%BC%9A%E5%88%86%E5%B1%82"><span class="nav-text">渲染的第四步：分层</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E7%9A%84%E7%AC%AC%E4%BA%94%E6%AD%A5%EF%BC%9A%E7%BB%98%E5%88%B6"><span class="nav-text">渲染的第五步：绘制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%86%E5%9D%97"><span class="nav-text">分块</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%85%89%E6%A0%85%E5%8C%96"><span class="nav-text">光栅化</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%BB%E2%80%94%E2%80%94Draw"><span class="nav-text">画——Draw</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B8%B2%E6%9F%93%E5%AE%8C%E6%95%B4%E8%BF%87%E7%A8%8B"><span class="nav-text">渲染完整过程</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9D%A2%E8%AF%95%E9%A2%98"><span class="nav-text">面试题</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFreflow%EF%BC%9F"><span class="nav-text">什么是reflow？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFrepaint%EF%BC%9F"><span class="nav-text">什么是repaint？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88transform%E6%95%88%E7%8E%87%E9%AB%98%EF%BC%9F"><span class="nav-text">为什么transform效率高？</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">鸣蜩十七</p>
  <div class="site-description" itemprop="description">记录我的学习、生活和恋爱日常</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">43</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">37</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="WEB前端大师课笔记 | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          WEB前端大师课笔记
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-10 21:02:29" itemprop="dateCreated datePublished" datetime="2023-06-10T21:02:29+08:00">2023-06-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-11 17:34:51" itemprop="dateModified" datetime="2023-06-11T17:34:51+08:00">2023-06-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="事件循环"><a href="#事件循环" class="headerlink" title="事件循环"></a>事件循环</h1><h2 id="浏览器的进程模型"><a href="#浏览器的进程模型" class="headerlink" title="浏览器的进程模型"></a>浏览器的进程模型</h2><h3 id="何为进程？"><a href="#何为进程？" class="headerlink" title="何为进程？"></a>何为进程？</h3><p>程序运行需要有它自己专属的内存空间，可以把这块内存空间理解为进程。</p>
<p>每个应用至少有一个进程，<strong>进程之间相互独立</strong>，即使要通信，也需要双方同意。</p>
<h3 id="何为线程？"><a href="#何为线程？" class="headerlink" title="何为线程？"></a>何为线程？</h3><p>有了进程后，就可以运行程序的代码了。</p>
<p>运行代码的“人”称之为“线程”。</p>
<p><strong>一个进程至少有一个线程</strong>，所以在进程开启后会自动创建一个线程来运行代码，该线程称之为<strong>主线程</strong>。</p>
<p>如果程序需要同时执行多块代码，主线程就会启动更多的线程来执行代码，所以一个进程可以包含多个线程。</p>
<h3 id="浏览器有哪些进程和线程？"><a href="#浏览器有哪些进程和线程？" class="headerlink" title="浏览器有哪些进程和线程？"></a>浏览器有哪些进程和线程？</h3><p><strong>浏览器是一个多进程多线程的应用程序</strong></p>
<p>浏览器内部工作极其复杂，为了避免相互影响，减少连环崩溃的几率，当启动浏览器后，它就会启动多个进程。</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/浏览器多进程多线程.png" alt="浏览器多进程多线程"></p>
<p>实际上浏览器有多个进程，图中只是举出三个作为例子而已。</p>
<p>其中，最主要的线程有：</p>
<ol>
<li><p>浏览器进程</p>
<p>主要负责界面显示、用户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。</p>
</li>
<li><p>网络进程</p>
<p>负责加载网络资源，网络进程内部会启动多个线程来处理不同的网络任务。</p>
</li>
<li><p><strong>渲染进程</strong>（重点）</p>
<p>渲染进程启动后，会开启一个<strong>渲染主线程</strong>，主线程负责执行HTML、CSS、JS代码。</p>
<p>默认情况下，<strong>浏览器会为每个标签页开启一个新的渲染进程</strong>，以保证不同的标签页之间不相互影响。（将来该默认模式可能会有所改变，具体参考Chrome官方文档）</p>
</li>
</ol>
<h2 id="渲染主线程是如何工作的"><a href="#渲染主线程是如何工作的" class="headerlink" title="渲染主线程是如何工作的"></a>渲染主线程是如何工作的</h2><p>渲染主线程是浏览器中最繁忙的线程，需要它处理的任务包括但不限于：</p>
<ul>
<li>解析HTML</li>
<li>解析CSS</li>
<li>计算样式</li>
<li>布局</li>
<li>处理图层</li>
<li>每秒把页面画60次</li>
<li>执行全局JS代码</li>
<li>执行事件处理函数</li>
<li>执行计时器的回调函数</li>
<li>······</li>
</ul>
<blockquote>
<p><sup><a href="#fn_" id="reffn_"></a></sup>: 思考题：为什么渲染进程不适用多个进程来处理这些事情？</p>
</blockquote>
<p>要处理这么多任务，主线程如何调度任务呢？</p>
<p>比如，正在执行一个JS函数，执行到一半用户点击了某个按钮，JS该立即去执行事件处理函数吗？</p>
<p>渲染主线程想了一个办法来处理这种问题：排队</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/排队.png" alt="排队"></p>
<ol>
<li>在最开始的时候，渲染主线程会进入一个无限循环</li>
<li>每一次循环会检查消息队列中是否有任务存在。如果有，就取出第一个任务执行，执行完一个后进入下一次循环;如果没有，则进入休眠状态。</li>
<li>其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时，如果主线程是休眠状态，则会将其唤醒以继续循环拿取任务</li>
</ol>
<h2 id="若干解释"><a href="#若干解释" class="headerlink" title="若干解释"></a>若干解释</h2><h3 id="何为异步"><a href="#何为异步" class="headerlink" title="何为异步"></a>何为异步</h3><p>代码在执行过程中，会遇到一些无法立即执行的任务，比如：</p>
<ul>
<li>及时完成后需要执行的任务 — <code>setTimeout</code></li>
<li>网络通信完成后需要执行的任务 — <code>XHR</code></li>
<li>用户操作后需要执行的任务 — <code>addEventListener</code></li>
</ul>
<p>如果让渲染主线程等待这些任务的时机达到，就会导致主线程长期处于「阻塞」的状态，从而导致浏览器「卡死」。</p>
<p>渲染主线程承担着极其重要的任务，无论如何都不能阻塞！</p>
<p>使用异步的方式，渲染主线程用不阻塞：</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/异步.png" alt="异步"></p>
<blockquote>
<p>面试题：如何理解JS的异步</p>
<p>答案： JS是一门单线程的语言，这是因为它运行在浏览器的渲染主线程中，而渲染主线程只有一个。而渲染主线程承担着诸多的工作，渲染页面、执行 JS 都在其中运行。<br>如果使用同步的方式，就极有可能导致主线程产生阻塞，从而导致消息队列中的很多其他任务无法得到执行。这样一来，一方面会导致繁忙的主线程白白的消耗时间，另一方面导致页面无法及时更新，给用户造成卡死现象。<br>所以浏览器采用异步的方式来避免。具体做法是当某些任务发生时，比如计时器、网络、事件监听，主线程将任务交给其他线程去处理，自身立即结束任务的执行，转而执行后续代码。当其他线程完成时，将事先传递的回调函数包装成任务，加入到消息队列的末尾排队，等待主线程调度执行。<br>在这种异步模式下，浏览器永不阻塞，从而最大限度的保证了单线程的流畅运行。</p>
</blockquote>
<h3 id="JS为何会阻塞渲染"><a href="#JS为何会阻塞渲染" class="headerlink" title="JS为何会阻塞渲染"></a>JS为何会阻塞渲染</h3><p>先看代码：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Mr.Yuan is awesome!<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span>&gt;</span>change<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> h1 = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"h1"</span>);</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> button = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"button"</span>);</span></span><br><span class="line"><span class="language-javascript">    </span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 死循环制定的时间</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params">duration</span>) {</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> start = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">while</span>(<span class="title class_">Date</span>.<span class="title function_">now</span>() - start &lt; duration) {}</span></span><br><span class="line"><span class="language-javascript">    }</span></span><br><span class="line"><span class="language-javascript">    </span></span><br><span class="line"><span class="language-javascript">    btn.<span class="property">onclick</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript">        h1.<span class="property">textContent</span> = <span class="string">"袁老师很帅"</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">delay</span>(<span class="number">3000</span>);</span></span><br><span class="line"><span class="language-javascript">    }</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<p>这段代码执行时，点击按钮后，并不是先改变文字再开始死循环，而是先死循环卡顿3000ms，再改变文字。具体细节如下：</p>
<ul>
<li>一开始渲染主线程将点击事件交给浏览器线程监听，此时渲染主线程无事可做。</li>
<li>当用户点击按钮后，浏览器线程将点击事件交给渲染主线程执行，点击事件中包括改变h1标签文字的绘制任务和死循环的delay任务。</li>
<li>渲染主线程执行h1标签文字，但需要绘制才能让用户看到，后面还要执行delay任务，所以此时将绘制任务添加到消息队列，当delay执行死循环结束后，渲染主线程才开始绘制任务，用户才看到文字的变动</li>
</ul>
<p>综上所述，其实文字是在死循环之前就改变了，但是需要等待死循环结束开始绘制任务，用户才能看到改动。</p>
<h3 id="任务有优先级吗"><a href="#任务有优先级吗" class="headerlink" title="任务有优先级吗"></a>任务有优先级吗</h3><p>任务没有优先级，在消息队列中先进先出。</p>
<p>但<strong>消息队列是有优先级的</strong>。</p>
<p>根据W3C的最新解释：</p>
<ul>
<li>每个任务都有一个任务类型，<strong>同一类型的任务必须在同一个队列</strong>，不同类型的任务可以分属不同的队列。在一次事件循环中，浏览器可以根据实际情况从不同的队列中取出任务执行。</li>
<li>浏览器必须准备好一个微队列，微队列中的任务优先所有其他任务执行。</li>
</ul>
<p>在目前chrome的实现中，至少包含了下面的队列：</p>
<ul>
<li>延时队列：用于存放计时器到达后的回调任务，优先级「中」</li>
<li>交互队列：用于存放用户操作后产生的事件处理任务，优先级「高」</li>
<li>微队列：用于存放需要最快执行的任务，优先级「最高」</li>
</ul>
<blockquote>
<p>添加任务到微队列的主要方式是使用 Promise、MutationObserver</p>
<p>例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(函数);</span><br></pre></td></tr></tbody></table></figure>
<p>面试题：请简述一下JS的事件循环</p>
<p>答案：事件循环又叫做消息循环，是浏览器渲染主线程的工作方式。<br>在Chrome的源码中，它开启一个不会结束的for循环，每次循环从消息队列中取出第一个任务执行，而其他线程只需要在合适的时候将任务加入到队列末尾即可。<br>过去把消息队列简单分为宏队列和微队列，这种说法目前已无法满足复杂的浏览器环境，取而代之的是一种更加灵活多变的处理方式。<br>根据W3C官方的解释，每个任务有不同的类型，同类型的任务必须在同一个队列，不同的任务可以属于不同的队列。不同任务队列有不同的优先级，在一次事件循环中，由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列，微队列的任务一定具有最高的优先级，必须优先调度执行。</p>
<p>面试题：JS 中的计时器能做到精确计时吗? 为什么?</p>
<p>答案：<strong>不行，因为:</strong></p>
<ol>
<li>操作系统的计时函数本身就有少量偏差，由由于JS 的计时器最终调用的是操作系统的函数，也就携带了 些偏差；</li>
<li>按照 W3C 的标准，浏览器实现计时器时，如果嵌套层级超过5层，则会带有4毫秒的延迟，在计时时间少于4毫秒时又带来了偏差；</li>
<li>受事件循环的影响，计时器的回调函数只能有在主线程空闲时运行，因此又带来了偏差。</li>
</ol>
</blockquote>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>单线程是异步产生的原因</p>
<p>事件循环是异步的实现方式</p>
<h1 id="浏览器渲染原理"><a href="#浏览器渲染原理" class="headerlink" title="浏览器渲染原理"></a>浏览器渲染原理</h1><h2 id="浏览器是如何渲染页面的"><a href="#浏览器是如何渲染页面的" class="headerlink" title="浏览器是如何渲染页面的"></a>浏览器是如何渲染页面的</h2><p>当浏览器的网络线程收到HTML文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。</p>
<p>在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。</p>
<p>整个渲染流程分为多个阶段，分别是: HTML 解析、样式计算、布局、分层、绘制、分块、光栅化、画</p>
<p>每个阶段都有明确的输入输出，上一个阶段的输 出会成为下一个阶段的输入。</p>
<p>这样，整个渲染流程就形成了一套组织严密的生产流水线：</p>
<p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染流水线.png" alt="渲染流水线"></p>
<h3 id="渲染的第一步：解析HTML——Parse-HTML"><a href="#渲染的第一步：解析HTML——Parse-HTML" class="headerlink" title="渲染的第一步：解析HTML——Parse HTML"></a>渲染的第一步：解析HTML——Parse HTML</h3><p>渲染的第一步就是解析HTML字符串。</p>
<p>解析过程中遇到CSS解析CSS, 遇到JS执行JS。 为了提高解析效率,浏览器在开始解析前，会启动一个预解析的线程，率先下载HTML 中的外部CSS文件和外部的JS文件。</p>
<p>如果主线程解析到<strong>link</strong>位置，此时外部的CSS文件还没有下载解析好，主线程不会等待，继续解析后续的HTML。这是因为下载和解析CSS的工作是在预解析线程中进行的。这就是CSS不会阻塞HTML解析的根本<br>原因。</p>
<p>如果主线程解析到<strong>script</strong>位置，会停止解析HTML，转而等待JS文件下载好，并将全局代码解析执行完成后，才能继续解析HTML。 这是因为JS代码的执行过程可能会修改当前的DOM树，所以DOM树的生成必须暂停。这就是JS会阻塞HTML 解析的根本原因。</p>
<p>第一步完成后，会得到DOM树和CSSOM树，浏览器的默认样式、内部样式、外部样式、行内样式均会包含<br>在CSSOM树中。</p>
<h3 id="渲染的第二步：样式计算——Recalculate-Style"><a href="#渲染的第二步：样式计算——Recalculate-Style" class="headerlink" title="渲染的第二步：样式计算——Recalculate Style"></a>渲染的第二步：样式计算——Recalculate Style</h3><p>渲染的下一步是样式计算。</p>
<p>主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式，称之为Computed Style。</p>
<p>在这一过程中，很多预设值会变成绝对值，比如red会变成rgb(255,0,0) ;相对单位会变成绝对单位，比如em会变成px。</p>
<p>这一步完成后，会得到一棵带有样式的DOM树。</p>
<h3 id="渲染的第三步：布局"><a href="#渲染的第三步：布局" class="headerlink" title="渲染的第三步：布局"></a>渲染的第三步：布局</h3><p>接下来是布局，布局完成后会得到布局树。</p>
<p>布局阶段会依次遍历DOM树的每一个节点，计算每个节点的几何信息。例如节点的宽高、相对包含块的位置。</p>
<p>大部分时候，DOM树和布局树并非一一对应。</p>
<p>比如display:none的节点没有几何信息，因此不会生成到布局树;又比如使用了伪元素选择器，虽然DOM树中不存在这些伪元素节点，但它们拥有几何信息，所以会生成到布局树中。还有<strong>匿名行盒</strong>、<strong>匿名块盒</strong>等等都会导致DOM树和布局树无法一一对应。</p>
<h3 id="渲染的第四步：分层"><a href="#渲染的第四步：分层" class="headerlink" title="渲染的第四步：分层"></a>渲染的第四步：分层</h3><p>下一步是分层</p>
<p>主线程会使用一套复杂的策略对整个布局树中进行分层。</p>
<p>分层的好处在于，将来某一个层改变后，仅会对该层进行后续处理，从而提升效率。</p>
<p>滚动条、堆叠上下文、transform、 opacity等样式都会或多或少的影响分层结果，也可以通过will-change属性更大程度的影响分层结果。</p>
<h3 id="渲染的第五步：绘制"><a href="#渲染的第五步：绘制" class="headerlink" title="渲染的第五步：绘制"></a>渲染的第五步：绘制</h3><p>再下一步是绘制</p>
<p>主线程会为每个层单独产生绘制指令集，用于描述这一层的内容该如何画出来。</p>
<h3 id="分块"><a href="#分块" class="headerlink" title="分块"></a>分块</h3><p>完成绘制后，主线程将每个图层的绘制信息提交给合成线程，剩余工作将由合成线程完成。</p>
<p>合成线程首先对每个图层进行分块，将其划分为更多的小区域。它会从线程池中拿取多个线程来完成分块工作。</p>
<h3 id="光栅化"><a href="#光栅化" class="headerlink" title="光栅化"></a>光栅化</h3><p>分块完成后，进入光栅化阶段。</p>
<p>合成线程会将块信息交给GPU 进程，以极高的速度完成光栅化。GPU进程会开启多个线程来完成光栅化，并且优先处理靠近视口区域的块。光栅化的结果，就是一块一块的位图</p>
<h3 id="画——Draw"><a href="#画——Draw" class="headerlink" title="画——Draw"></a>画——Draw</h3><p>最后一个阶段就是画了。</p>
<p>合成线程拿到每个层、每个块的位图后，生成一个个「指引(quad) 」信息。</p>
<p>指引会标识出每个位图应该画到屏幕的哪个位置，以及会考虑到旋转、缩放等变形。</p>
<p>变形（<code>transform</code>）发生在合成线程，与渲染主线程无关，这就是transform效率高的本质原因。</p>
<p>合成线程会把quad提交给GPU 进程，由GPU 进程产生系统调用，提交给GPU 硬件，完成最终的屏幕成像。</p>
<h3 id="渲染完整过程"><a href="#渲染完整过程" class="headerlink" title="渲染完整过程"></a>渲染完整过程</h3><p><img src="/2023/06/10/WEB%E5%89%8D%E7%AB%AF%E5%A4%A7%E5%B8%88%E8%AF%BE%E7%AC%94%E8%AE%B0/渲染完整过程.png" alt="渲染完整过程"></p>
<h2 id="面试题"><a href="#面试题" class="headerlink" title="面试题"></a>面试题</h2><h3 id="什么是reflow？"><a href="#什么是reflow？" class="headerlink" title="什么是reflow？"></a>什么是reflow？</h3><p>reflow的本质就是重新计算layout 树。</p>
<p>当进行了会影响布局树的操作后，需要重新计算布局树，会引发layout。</p>
<p>为了避免连续的多次操作导致布局树反复计算，浏览器会合并这些操作，当JS代码全部完成后再进行统一计算。所以，改动属性造成的reflow 是异步完成的。</p>
<p>也同样因为如此，当JS获取布局属性时，就可能造成无法获取到最新的布局信息。浏览器在反复权衡下，最终决定获取属性立即reflow。</p>
<h3 id="什么是repaint？"><a href="#什么是repaint？" class="headerlink" title="什么是repaint？"></a>什么是repaint？</h3><p>repaint的本质就是重新根据分层信息计算了绘制指令。</p>
<p>当改动了可见样式后，就需要重新计算，会引发repaint。</p>
<p>由于元素的布局信息也属于可见样式，所以reflow一定会引起repaint。</p>
<h3 id="为什么transform效率高？"><a href="#为什么transform效率高？" class="headerlink" title="为什么transform效率高？"></a>为什么transform效率高？</h3><p>因为transform 既不会影响布局也不会影响绘制指令，它影响的只是渲染流程的最后- -个「draw」 阶段。</p>
<p>由于draw 阶段在合成线程中，所以transform 的变化几乎不会影响渲染主线程。反之，渲染主线程无论<br>如何忙碌，也不会影响transform 的变化。</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF/" rel="tag"># -事件循环</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2023/06/03/%E9%A1%B5%E5%BC%8F%E5%AD%98%E5%82%A8/" rel="prev" title="">
                  <i class="fa fa-chevron-left"></i> 
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2023/06/13/Java%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="next" title="Java：多线程">
                  Java：多线程 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">鸣蜩十七</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  




  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>



</body>
</html>
